﻿/*This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
	*/

using System;
using System.Text;
using System.IO;

namespace ZipLib
{
    /// <summary>
    /// Normal entry header for each entry
    /// </summary>
    public class ZipEntryHeader
    {
        /// <summary>
        /// Version needed to extract (minimum)
        /// </summary>
        public short Version { get; set; }

        /// <summary>
        /// General purpose bit flag
        /// </summary>
        public short Flags { get; set; }

        /// <summary>
        /// Compression method
        /// </summary>
        protected internal short Compression { get; set; }

        /// <summary>
        /// File last modification time. Stored in standard MS-DOS format.
        /// </summary>
        protected internal short ModTime { get; set; }

        /// <summary>
        /// File last modification date. Stored in standard MS-DOS format.
        /// </summary>
        protected internal short ModDate { get; set; }

        /// <summary>
        /// File date time
        /// </summary>
        public DateTime ModDateTime
        {
            get
            {
                int second = ModTime & 0x001F/2;
                int minute = (ModTime & 0x07E0) >> 5;
                int hour = ModTime >> 11;

                int day = ModDate & 0x001F;
                int month = (ModDate & 0x01E0) >> 5;
                int year = (ModDate >> 9) + 1980;

                return new DateTime(year, month, day, hour, minute, second);
            }
            set
            {
                int modtime = (value.Second << 1) | (value.Minute << 5) | (value.Hour << 11);
                int modData = (value.Day) | (value.Month << 5) | ((value.Year - 1980) << 9);
                ModTime = (short) modtime;
                ModDate = (short) modData;
            }
        }

        /// <summary>
        /// CRC-32. Value computed over file data by CRC-32 algorithm with 'magic number' 0xdebb20e3.
        /// </summary>
        public UInt32 Crc32 { get; set; }

        /// <summary>
        /// Compressed size. If archive is in ZIP64 format, this filed is 0xffffffff and the length is stored in the extra field
        /// </summary>
        public UInt32 CompressedSize { get; protected internal set; }

        /// <summary>
        /// Uncompressed size. If archive is in ZIP64 format, this filed is 0xffffffff and the length is stored in the extra field
        /// </summary>
        public UInt32 UncompressedSize { get; protected internal set; }

        /// <summary>
        /// File name length
        /// </summary>
        public UInt16 FileNameLength { get; protected internal set; }

        /// <summary>
        /// Extra field length
        /// </summary>
        public UInt16 ExtraFieldLength { get; protected internal set; }

        /// <summary>
        /// File name. The name of the file including an optional relative path. 
        /// All slashes in the path should be forward slashes '/'.
        /// </summary>
        public string FileName
        {
            get { return _fileName; }
            set
            {
                _fileName = value;
                FileNameLength = (UInt16) value.Length;
            }
        }

        /// <summary>
        /// Extra field. Used to store additional information
        /// </summary>
        public Byte[] ExtraFiled
        {
            get { return ExtraField; }
            set
            {
                ExtraField = value;
                ExtraFieldLength = (UInt16) value.Length;
            }
        }

        private string _fileName;
        protected internal Byte[] ExtraField;
        protected internal long StreamPosition { get; internal set; }

        protected internal EntryType EntryType
        {
            get
            {
                if (UncompressedSize == 0 && UncompressedSize == CompressedSize)
                {
                    return EntryType.Directory;
                }
                return EntryType.File;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ZipEntryHeader"/>
        /// </summary>
        /// <param name="r">BinaryReader containing Zip stream</param>
        public ZipEntryHeader(BinaryReader r)
        {
            if (r.ReadInt32() != 0x04034B50) throw new InvalidDataException();
            Version = r.ReadInt16();
            Flags = r.ReadInt16();
            Compression = r.ReadInt16();
            if (Compression != 0x0 && Compression != 0x8) throw new ZipException("Compression algorithm not supported");
            ModTime = r.ReadInt16();
            ModDate = r.ReadInt16();
            Crc32 = r.ReadUInt32();
            CompressedSize = r.ReadUInt32();
            UncompressedSize = r.ReadUInt32();
            FileNameLength = r.ReadUInt16();
            ExtraFieldLength = r.ReadUInt16();
            FileName = Encoding.ASCII.GetString(r.ReadBytes(FileNameLength));
            ExtraFiled = r.ReadBytes(ExtraFieldLength);
            StreamPosition = r.BaseStream.Position;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ZipEntryHeader"/>
        /// </summary>
        public ZipEntryHeader()
        {
            Version = 0x14;
            Compression = 0x08;
            ModDateTime = DateTime.Now;
        }
    }
}